/******************************************************************************
 * Copyright 2015 Ai-Thinker Co.,Ltd.
 *
 * FileName: my9291.c
 *
 * Description: MY9291 Driver
 *
 * Modification history:
 *     2015/09/10, v1.0 create this file.
*******************************************************************************/

#include "osapi.h"
#include "user_config.h"
#include "user_interface.h"
#include "driver/pin_map.h"
#include "gpio.h"
#include "driver/my9291.h"

#ifndef LOW
#define LOW 							(0)
#endif /* ifndef LOW */

#ifndef HIGH
    #define HIGH 							(1)
#endif /* ifndef HIGH */

#define MY9291_DIRECT_GPIO(pin) 		PIN_FUNC_SELECT(pin_name[pin], pin_func[pin])
#define MY9291_DIRECT_READ(pin) 		(0x01 & GPIO_INPUT_GET(GPIO_ID_PIN(pin)))
#define MY9291_DIRECT_MODE_INPUT(pin) 	(GPIO_DIS_OUTPUT(GPIO_ID_PIN(pin)))
#define MY9291_DIRECT_MODE_OUTPUT(pin)
#define MY9291_DIRECT_WRITE_LOW(pin) 	(GPIO_OUTPUT_SET(GPIO_ID_PIN(pin), LOW))
#define MY9291_DIRECT_WRITE_HIGH(pin) 	(GPIO_OUTPUT_SET(GPIO_ID_PIN(pin), HIGH))

LOCAL my9291_cmd_t my9291_commands[GPIO_MAX_INDEX + 1];

void ICACHE_FLASH_ATTR
my9291_di_pulse(uint8_t pin_di, uint16_t times)
{
    uint16_t i;
    for (i = 0; i < times; i++)
    {
        MY9291_DIRECT_WRITE_HIGH(pin_di);
        MY9291_DIRECT_WRITE_LOW(pin_di);
    }
}

void ICACHE_FLASH_ATTR
my9291_dcki_pulse(uint8_t pin_dcki, uint16_t times)
{
    uint16_t i;
    for (i = 0; i < times; i++)
    {
        MY9291_DIRECT_WRITE_HIGH(pin_dcki);
        MY9291_DIRECT_WRITE_LOW(pin_dcki);
    }
}

//-----------------------------------------------------------------------------
// Command Data
//-----------------------------------------------------------------------------
void ICACHE_FLASH_ATTR
my9291_send_command(uint8_t pin_di, uint8_t pin_dcki, my9291_cmd_t command)
{
    uint8_t i;
    uint8_t command_data = *(uint8_t *)(&command);
    my9291_commands[pin_dcki] = command;

    // ets_intr_lock();
    // TStop > 12us.
    os_delay_us(12);
    // Send 12 DI pulse, after 6 pulse's falling edge store duty data, and 12
    // pulse's rising edge convert to command mode.
    my9291_di_pulse(pin_di, 12);
    // Delay >12us, begin send CMD data
    os_delay_us(12);
    // Send CMD data

    for (i = 0; i < 4; i++)
    {
        // DCK = 0;
        MY9291_DIRECT_WRITE_LOW(pin_dcki);
        if (command_data & 0x80)
        {
            // DI = 1;
            MY9291_DIRECT_WRITE_HIGH(pin_di);
        }
        else
        {
            // DI = 0;
            MY9291_DIRECT_WRITE_LOW(pin_di);
        }
        // DCK = 1;
        MY9291_DIRECT_WRITE_HIGH(pin_dcki);
        command_data = command_data << 1;
        if (command_data & 0x80)
        {
            // DI = 1;
            MY9291_DIRECT_WRITE_HIGH(pin_di);
        }
        else
        {
            // DI = 0;
            MY9291_DIRECT_WRITE_LOW(pin_di);
        }
        // DCK = 0;
        MY9291_DIRECT_WRITE_LOW(pin_dcki);
        // DI = 0;
        MY9291_DIRECT_WRITE_LOW(pin_di);
        command_data = command_data << 1;
    }

    // TStart > 12us. Delay 12 us.
    os_delay_us(12);
    // Send 16 DI pulse，at 14 pulse's falling edge store CMD data, and
    // at 16 pulse's falling edge convert to duty mode.
    my9291_di_pulse(pin_di, 16);
    // TStop > 12us.
    os_delay_us(12);
    // ets_intr_unlock();
}

//-----------------------------------------------------------------------------
// Image Data
//-----------------------------------------------------------------------------
void ICACHE_FLASH_ATTR
my9291_send_duty(uint8_t pin_di, uint8_t pin_dcki, uint16_t duty_r, uint16_t duty_g, uint16_t duty_b, uint16_t duty_w)
{
    uint8_t i = 0;
    uint8_t channel = 0;
    uint8_t bit_length = 8;
    uint16_t duty_current = 0;

    // Definition for RGBW channels
    uint16_t duty[4] = { duty_r, duty_g, duty_b, duty_w };

    switch (my9291_commands[pin_dcki].bit_width)
    {
    case my9291_cmd_bit_width_16:
        bit_length = 16;
        break;
    case my9291_cmd_bit_width_14:
        bit_length = 14;
        break;
    case my9291_cmd_bit_width_12:
        bit_length = 12;
        break;
    case my9291_cmd_bit_width_8:
        bit_length = 8;
        break;
    default:
        bit_length = 8;
        break;
    }

    // ets_intr_lock();
    // TStop > 12us.
    os_delay_us(12);

    for (channel = 0; channel < 4; channel++) //RGBW 4CH
    {
        // RGBW Channel
        duty_current = duty[channel];
        // Send 8bit/12bit/14bit/16bit Data
        for (i = 0; i < bit_length / 2; i++)
        {
            // DCK = 0;
            MY9291_DIRECT_WRITE_LOW(pin_dcki);
            if (duty_current & (0x01 << (bit_length - 1)))
            {
                // DI = 1;
                MY9291_DIRECT_WRITE_HIGH(pin_di);
            }
            else
            {
                // DI = 0;
                MY9291_DIRECT_WRITE_LOW(pin_di);
            }
            // DCK = 1;
            MY9291_DIRECT_WRITE_HIGH(pin_dcki);
            duty_current = duty_current << 1;
            if (duty_current & (0x01 << (bit_length - 1)))
            {
                // DI = 1;
                MY9291_DIRECT_WRITE_HIGH(pin_di);
            }
            else
            {
                // DI = 0;
                MY9291_DIRECT_WRITE_LOW(pin_di);
            }
            //DCK = 0;
            MY9291_DIRECT_WRITE_LOW(pin_dcki); 
            //DI = 0;
            MY9291_DIRECT_WRITE_LOW(pin_di);
            duty_current = duty_current << 1;
        }
    }

    // TStart > 12us. Ready for send DI pulse.
    os_delay_us(12);
    // Send 8 DI pulse. After 8 pulse falling edge, store old data.
    my9291_di_pulse(pin_di, 8);
    // TStop > 12us.
    os_delay_us(12);
    // ets_intr_unlock();
}

void ICACHE_FLASH_ATTR
my9291_init(uint8_t pin_di, uint8_t pin_dcki, my9291_cmd_t command)
{
    MY9291_DIRECT_GPIO(pin_di);
    MY9291_DIRECT_GPIO(pin_dcki);
    MY9291_DIRECT_WRITE_LOW(pin_di);
    MY9291_DIRECT_WRITE_LOW(pin_dcki);
    MY9291_DIRECT_MODE_OUTPUT(pin_di);
    MY9291_DIRECT_MODE_OUTPUT(pin_dcki);
    // Clear all duty register
    my9291_dcki_pulse(pin_dcki, 64 / 2);
    my9291_send_command(pin_di, pin_dcki, command);
}

